package zuo.biao.apijson.parser.builder;

import cn.hutool.core.util.StrUtil;
import zuo.biao.apijson.parser.ParserDomain;
import zuo.biao.apijson.parser.SQLProviderException;
import zuo.biao.apijson.parser.SafeAppendable;
import zuo.biao.apijson.parser.provider.SQLProvider;
import zuo.biao.apijson.parser.Utils;

import java.util.ArrayList;
import java.util.List;

public class SQLBuilder implements Builder {
    private static final String AND = ") \nAND (";
    private static final String OR = ") \nOR (";


    @Override
    public ParserDomain selectSQL(Appendable appendable, SQLProvider sqlProvider) {
        SafeAppendable builder = new SafeAppendable(appendable);
        //
        if (sqlProvider.isDistinct()) {
            sqlClause(builder, "SELECT DISTINCT", sqlProvider.getSelect(), "", "", ", ");
        } else {
            sqlClause(builder, "SELECT", sqlProvider.getSelect(), "", "", ", ");
        }
        //
        sqlClause(builder, "FROM", sqlProvider.getTables(), "", "", ", ");
        joins(builder,sqlProvider);
        sqlClause(builder, "WHERE", sqlProvider.getWhere(), "(", ")", " AND ");
        sqlClause(builder, "GROUP BY", sqlProvider.getGroupBy(), "", "", ", ");
        sqlClause(builder, "HAVING", sqlProvider.getHaving(), "(", ")", " AND ");
        sqlClause(builder, "ORDER BY", sqlProvider.getOrderBy(), "", "", ", ");
        return new ParserDomain(builder.toString(),sqlProvider.getPreperedName(),sqlProvider.getPreperedValue());
    }



    @Override
    public ParserDomain insertSQL(Appendable appendable, SQLProvider sqlProvider) {
        SafeAppendable builder = new SafeAppendable(appendable);
        sqlClause(builder, "INSERT INTO", sqlProvider.getTables(), "", "", "");
        sqlClause(builder, "", sqlProvider.getColumns(), "(", ")", ", ");
        sqlClause(builder, "VALUES", sqlProvider.getValues(), "(", ")", ", ");
        return new ParserDomain(builder.toString(),sqlProvider.getPreperedName(),sqlProvider.getPreperedValue());
    }

    @Override
    public ParserDomain deleteSQL(Appendable appendable, SQLProvider sqlProvider) {
        SafeAppendable builder = new SafeAppendable(appendable);
        sqlClause(builder, "DELETE FROM", sqlProvider.getTables(), "", "", "");
        sqlClause(builder, "WHERE", sqlProvider.getWhere(), "(", ")", " AND ");
        return new ParserDomain(builder.toString(),sqlProvider.getPreperedName(),sqlProvider.getPreperedValue());
    }

    @Override
    public ParserDomain updateSQL(Appendable appendable, SQLProvider sqlProvider) {
        SafeAppendable builder = new SafeAppendable(appendable);
        sqlClause(builder, "UPDATE", sqlProvider.getTables(), "", "", "");
        joins(builder,sqlProvider);
        sqlClause(builder, "SET", sqlProvider.getSets(), "", "", ", ");
        sqlClause(builder, "WHERE", sqlProvider.getWhere(), "(", ")", " AND ");
        return new ParserDomain(builder.toString(),sqlProvider.getPreperedName(),sqlProvider.getPreperedValue());
    }


    private void sqlClause(SafeAppendable builder, String keyword, List<String> parts, String open, String close,
                           String conjunction) {
        if ( parts != null &&  !parts.isEmpty()) {
            if (!builder.isEmpty()) {
                builder.append("\n");
            }
            builder.append(keyword);
            builder.append(" ");
            builder.append(open);
            String last = "________";
            for (int i = 0, n = parts.size(); i < n; i++) {
                String part = parts.get(i);

                if (i > 0 && !StrUtil.equalsAny(part,AND,OR) && !StrUtil.equalsAny(last,AND,OR)) {
                    builder.append(conjunction);
                }
                builder.append(part);
                last = part;
            }
            builder.append(close);
        }
    }
    private void joins(SafeAppendable builder,SQLProvider sqlProvider) {
        sqlClause(builder, "JOIN", sqlProvider.getJoin(), "", "", "\nJOIN ");
        sqlClause(builder, "INNER JOIN", sqlProvider.getInnerJoin(), "", "", "\nINNER JOIN ");
        sqlClause(builder, "OUTER JOIN", sqlProvider.getOuterJoin(), "", "", "\nOUTER JOIN ");
        sqlClause(builder, "LEFT OUTER JOIN", sqlProvider.getLeftOuterJoin(), "", "", "\nLEFT OUTER JOIN ");
        sqlClause(builder, "RIGHT OUTER JOIN", sqlProvider.getRightOuterJoin(), "", "", "\nRIGHT OUTER JOIN ");
    }

}
